/**
 *
 */
package edu.tjut.tjutcommunity.recommend.UserBasedCollaborativeRecommender;

import edu.tjut.tjutcommunity.recommend.algorithms.RecommendAlgorithm;
import edu.tjut.tjutcommunity.recommend.algorithms.RecommendKit;
import edu.tjut.tjutcommunity.entity.Postlogs;
import edu.tjut.tjutcommunity.mapper.PostlogsMapper;
import org.apache.log4j.Logger;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLBooleanPrefJDBCDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * Collaborative-Based Filter 基于用户的协同过滤
 *
 */
@Component
public class MahoutUserBasedCollaborativeRecommender implements RecommendAlgorithm {
    public static final Logger logger = Logger.getLogger(MahoutUserBasedCollaborativeRecommender.class);

    /**
     * 对应计算相似度时的时效天数
     */
    @Value("${recommend.CFValidDay}")
    private int inRecDays;

    /**
     * 给每个用户推荐的帖子的条数
     */
    @Value("${recommend.CFRecNum}")
    public int N;



    //传入mysql数据源。
    final
    DataSource dataSource;
    final
    PostlogsMapper postlogsMapper;


    public MahoutUserBasedCollaborativeRecommender(DataSource dataSource, PostlogsMapper postlogsMapper) {
        this.dataSource = dataSource;
        this.postlogsMapper = postlogsMapper;
    }

    /**
     * 给特定的一批用户进行帖子推荐
     *
     * @param
     */
    @SuppressWarnings("unused")
    @Override
    public void recommend(List<Integer> users) {
        int count = 0;
        try {
            System.out.println("CF start at " + new Date());
            MySQLBooleanPrefJDBCDataModel dataModel = new MySQLBooleanPrefJDBCDataModel(dataSource,
                    "postlogs",
                    "uid",
                    "pid",
                    "create_time");

            List<Postlogs> postlogsList = postlogsMapper.selectList(null);

            // 移除过期的用户浏览新闻行为，这些行为对计算用户相似度不再具有较大价值
            for (Postlogs postlogs : postlogsList) {
                if (postlogs.getCreateTime().before(RecommendKit.getInRecTimestamp(inRecDays))) {
                    dataModel.removePreference(postlogs.getUid(), postlogs.getPid());
                    postlogsMapper.deleteById(postlogs);
                }
            }

            UserSimilarity similarity = new LogLikelihoodSimilarity(dataModel);

            // NearestNeighborhood的数量有待考察
            UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);

            Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);

            for (Integer user : users) {
                long start = System.currentTimeMillis();
                //用户ID
                LongPrimitiveIterator otherusersIterator = dataModel.getUserIDs();
                //遍历用户ID，计算任何两个用户的相似度
                while (otherusersIterator.hasNext()) {
                    long otherUserID = otherusersIterator.nextLong();
                }
                List<RecommendedItem> recItems = recommender.recommend(user, N);

                Set<Integer> hs = new HashSet<Integer>();

                for (RecommendedItem recItem : recItems) {
                    hs.add((int) recItem.getItemID());
                }

                // 过滤掉已推荐帖子和已过期帖子
                RecommendKit.filterOutDatePosts(hs);
                RecommendKit.filterReccedPosts(hs, user);


                if (hs.size() > N) {
                    RecommendKit.removeOverPosts(hs, N);
                }

                RecommendKit.insertRecommend(user, hs.iterator(), RecommendAlgorithm.CF);

                count += hs.size();
            }
        } catch (TasteException e) {
            logger.error("CB算法构造偏好对象失败！");
            e.printStackTrace();
        } catch (Exception e) {
            logger.error("CB算法数据库操作失败！");
            e.printStackTrace();
        }
        System.out.println("CF has contributed " + (count / users.size()) + " recommending post on average");
        System.out.println("CF finish at " + new Date());

    }

    public int getRecNums() {
        return N;
    }

    public void setRecNums(int recNums) {
        N = recNums;
    }

}
